// Copyright (c) 2011, Regents of the University of Utah
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef SLARRAY_H
#define SLARRAY_H
#include <iostream>
#include "slIO.H"

template <typename T>
class SlArray3D {
public:
    T * data;
    unsigned int _nx, _ny, _nz, nynz;
public:
    inline T &operator()(const unsigned int &i, const unsigned int &j, const unsigned int &k);
    inline T &operator()(const unsigned int &i, const unsigned int &j, const unsigned int &k) const;
    /*inline*/ const T &operator=(const T &d);
    inline const SlArray3D<T> &operator=(const SlArray3D<T> &d);
    inline SlArray3D();
    inline SlArray3D(const unsigned int &nx, const unsigned int &ny, const unsigned int &nz);
    inline ~SlArray3D();
    inline bool allocate(const unsigned int &nx, const unsigned int &ny, const unsigned int &nz);
    unsigned int nx() const { return _nx; };
    unsigned int ny() const { return _ny; };
    unsigned int nz() const { return _nz; };
};

template <typename T> std::istream &operator>>(std::istream &strm, SlArray3D<T> &v);
template <typename T> std::ostream &operator<<(std::ostream &strm, const SlArray3D<T> &v);

template <typename T>
inline bool SlArray3D<T>::allocate(const unsigned int &x, const unsigned int &y, const unsigned int &z) {
    if (data != NULL && _nx == x && _ny == y && _nz == z) return true;
    _nx = x;
    _ny = y;
    _nz = z;
    nynz = _ny * _nz;
    if (data) delete[]data;
    data = new T[_nx*nynz];
    return true;
}

template<class T>
inline SlArray3D<T> operator*(const SlArray3D<T> &d, T x) {
    SlArray3D<T> y(d.nx(), d.ny(), d.nz());
    for (unsigned int i = 0; i<d.nx(); i++)
        for (unsigned int j = 0; j<d.ny(); j++)
            for (unsigned int k = 0; k<d.nz(); k++)
                y(i, j, k) = d(i, j, k)*x;
    return y;
}

template <class T>
/*inline*/ const T &SlArray3D<T>::operator=(const T &x) {
    T *d = data;
    for (unsigned int i = 0; i<_nx; i++) {
        for (unsigned int j = 0; j<_ny; j++) {
            for (unsigned int k = 0; k<_nz; k++, d++) {
                (*d) = x;
            }
        }
    }
    return x;
}


template <class T>
inline const SlArray3D<T> &SlArray3D<T>::operator=(const SlArray3D<T> &x) {
    T *d = data;
    T *y = x.data;
    for (unsigned int i = 0; i<_nx; i++) {
        for (unsigned int j = 0; j<_ny; j++) {
            for (unsigned int k = 0; k<_nz; k++, d++, y++) {
                (*d) = (*y);
            }
        }
    }
    return x;
}

template <class T>
inline T &SlArray3D<T>::operator()(const unsigned int &i, const unsigned int &j, const unsigned int &k) {
    return data[i*nynz + j * _nz + k];
}

template <class T>
inline T &SlArray3D<T>::operator()(const unsigned int &i, const unsigned int &j, const unsigned int &k) const {
    return data[i*nynz + j * _nz + k];
}

template <class T>
inline SlArray3D<T>::SlArray3D() {
    data = NULL;
}

template <class T>
inline SlArray3D<T>::SlArray3D(const unsigned int &x, const unsigned int &y, const unsigned int &z) {
    data = NULL;
    allocate(x, y, z);
}

template <class T>
inline SlArray3D<T>::~SlArray3D() {
    if (data) delete[] data;
}

template <class T>
std::istream &operator>>(std::istream &strm, SlArray3D<T> &v) {
    unsigned int nx, ny, nz, i, j, k;
    std::ios::fmtflags orgFlags = strm.setf(std::ios::skipws);

    eatStr("[", strm);
    strm >> nx;
    eatStr(",", strm);
    strm >> ny;
    eatStr(",", strm);
    strm >> nz;
    eatStr("]", strm);

    eatStr("[", strm);
    v.allocate(nx, ny, nz);
    for (i = 0; i<nx; i++) {
        for (j = 0; j<ny; j++) {
            for (k = 0; k<nz; k++) {
                strm >> v(i, j, k);
            }
        }
    }
    eatStr("]", strm);
    strm.flags(orgFlags);
    return strm;
}

template <typename T>
std::ostream &operator<<(std::ostream &strm, const SlArray3D<T> &v) {
    strm << "[";
    strm << v.nx() << "," << v.ny() << "," << v.nz() << "]";
    unsigned int i, j, k;
    strm << "[" << std::endl;
    for (i = 0; i<v.nx(); i++) {
        for (j = 0; j<v.ny(); j++) {
            for (k = 0; k<v.nz(); k++) {
                strm << v(i, j, k) << " ";
            }
        }
    }
    strm << "]\n";
    return strm;
}
#endif

